Maîtrisez createRef de React pour manipuler le DOM et les composants. Découvrez son utilisation efficace dans les composants de classe pour le focus, les médias et les intégrations.
React createRef : Le guide définitif pour les interactions directes avec les composants et les éléments du DOM
Dans le paysage vaste et souvent complexe du développement web moderne, React s'est imposé comme une force dominante, principalement célébrée pour son approche déclarative de la construction d'interfaces utilisateur. Ce paradigme encourage les développeurs à décrire ce que leur interface utilisateur devrait être en fonction des données, plutôt que de prescrire comment atteindre cet état visuel par des manipulations directes du DOM. Cette abstraction a considérablement simplifié le développement d'interfaces utilisateur, rendant les applications plus prévisibles, plus faciles à raisonner et très performantes.
Cependant, le monde réel des applications web est rarement entièrement déclaratif. Il existe des scénarios spécifiques, mais courants, où l'interaction directe avec l'élément sous-jacent du DOM (Document Object Model) ou une instance de composant de classe devient non seulement pratique, mais absolument nécessaire. Ces "échappatoires" au flux déclaratif de React sont connues sous le nom de refs. Parmi les différents mécanismes que React propose pour créer et gérer ces références, React.createRef() se présente comme une API fondamentale, particulièrement pertinente pour les développeurs travaillant avec des composants de classe.
Ce guide complet se veut votre ressource définitive pour comprendre, implémenter et maîtriser React.createRef(). Nous nous lancerons dans une exploration détaillée de son objectif, nous plongerons dans sa syntaxe et ses applications pratiques, nous mettrons en lumière ses meilleures pratiques et nous le distinguerons des autres stratégies de gestion des refs. Que vous soyez un développeur React chevronné cherchant à consolider votre compréhension des interactions impératives ou un nouveau venu cherchant à saisir ce concept crucial, cet article vous dotera des connaissances nécessaires pour construire des applications React plus robustes, performantes et globalement accessibles, qui gèrent avec élégance les exigences complexes des expériences utilisateur modernes.
Comprendre les Refs dans React : faire le pont entre les mondes déclaratif et impératif
À la base, React prône un style de programmation déclaratif. Vous définissez vos composants, leur état et la manière dont ils s'affichent. React prend ensuite le relais, mettant à jour efficacement le DOM réel du navigateur pour refléter votre interface utilisateur déclarée. Cette couche d'abstraction est immensément puissante, protégeant les développeurs des complexités et des pièges de performance de la manipulation directe du DOM. C'est pourquoi les applications React semblent souvent si fluides et réactives.
Le flux de données unidirectionnel et ses limites
La force architecturale de React réside dans son flux de données unidirectionnel. Les données circulent de manière prévisible du haut vers le bas, des composants parents aux enfants via les props, et les changements d'état au sein d'un composant déclenchent de nouveaux rendus qui se propagent à travers son sous-arbre. Ce modèle favorise la prévisibilité et facilite considérablement le débogage, car vous savez toujours d'où proviennent les données et comment elles influencent l'interface utilisateur. Cependant, toutes les interactions ne s'alignent pas parfaitement sur ce flux de données descendant.
Considérez des scénarios comme :
- Mettre le focus par programmation sur un champ de saisie lorsqu'un utilisateur navigue vers un formulaire.
- Déclencher les méthodes
play()oupause()sur un élément<video>. - Mesurer les dimensions exactes en pixels d'un
<div>rendu pour ajuster dynamiquement la mise en page. - Intégrer une bibliothèque JavaScript tierce complexe (par exemple, une bibliothèque de graphiques comme D3.js ou un outil de visualisation de cartes) qui attend un accès direct à un conteneur DOM.
Ces actions sont intrinsèquement impératives – elles impliquent de commander directement à un élément de faire quelque chose, plutôt que de simplement déclarer son état souhaité. Bien que le modèle déclaratif de React puisse souvent abstraire de nombreux détails impératifs, il n'élimine pas complètement leur nécessité. C'est précisément là que les refs entrent en jeu, fournissant une échappatoire contrôlée pour effectuer ces interactions directes.
Quand utiliser les Refs : naviguer entre les interactions impératives et déclaratives
Le principe le plus important lorsque l'on travaille avec des refs est de les utiliser avec parcimonie et uniquement lorsque c'est absolument nécessaire. Si une tâche peut être accomplie en utilisant les mécanismes déclaratifs standards de React (état et props), cela devrait toujours être votre approche préférée. Une dépendance excessive aux refs peut conduire à un code plus difficile à comprendre, à maintenir et à déboguer, sapant les avantages mêmes que React offre.
Cependant, pour les situations qui nécessitent réellement un accès direct à un nœud DOM ou à une instance de composant, les refs sont la solution correcte et prévue. Voici une ventilation plus détaillée des cas d'utilisation appropriés :
- Gestion du focus, de la sélection de texte et de la lecture multimédia : Ce sont des exemples classiques où vous devez interagir impérativement avec des éléments. Pensez à la mise au point automatique d'une barre de recherche au chargement de la page, à la sélection de tout le texte dans un champ de saisie, ou au contrôle de la lecture d'un lecteur audio ou vidéo. Ces actions sont généralement déclenchées par des événements utilisateur ou des méthodes du cycle de vie des composants, et non simplement en changeant les props ou l'état.
- Déclenchement d'animations impératives : Bien que de nombreuses animations puissent être gérées de manière déclarative avec des transitions/animations CSS ou des bibliothèques d'animation React, certaines animations complexes et à haute performance, en particulier celles impliquant l'API HTML Canvas, WebGL, ou nécessitant un contrôle fin des propriétés des éléments qui sont mieux gérées en dehors du cycle de rendu de React, peuvent nécessiter des refs.
- Intégration avec des bibliothèques DOM tierces : De nombreuses bibliothèques JavaScript vénérables (par exemple, D3.js, Leaflet pour les cartes, divers anciens kits d'outils d'interface utilisateur) sont conçues pour manipuler directement des éléments DOM spécifiques. Les refs fournissent le pont essentiel, permettant à React de rendre un élément conteneur, puis d'accorder à la bibliothèque tierce l'accès à ce conteneur pour sa propre logique de rendu impérative.
-
Mesure des dimensions ou de la position d'un élément : Pour implémenter des mises en page avancées, la virtualisation ou des comportements de défilement personnalisés, vous avez souvent besoin d'informations précises sur la taille d'un élément, sa position par rapport à la fenêtre d'affichage ou sa hauteur de défilement. Des API comme
getBoundingClientRect()ne sont accessibles que sur les nœuds DOM réels, ce qui rend les refs indispensables pour de tels calculs.
Inversement, vous devriez éviter d'utiliser des refs pour des tâches qui peuvent être réalisées de manière déclarative. Cela inclut :
- Modifier le style d'un composant (utilisez l'état pour le style conditionnel).
- Changer le contenu textuel d'un élément (passez-le en tant que prop ou mettez à jour l'état).
- Communication complexe entre composants (les props et les rappels sont généralement supérieurs).
- Tout scénario où vous essayez de répliquer la fonctionnalité de la gestion d'état.
Plongée dans React.createRef() : l'approche moderne pour les composants de classe
React.createRef() a été introduit dans React 16.3, offrant un moyen plus explicite et plus propre de gérer les refs par rapport aux anciennes méthodes comme les refs par chaîne (maintenant dépréciées) et les refs par rappel (toujours valides mais souvent plus verbeuses). Il est conçu pour être le principal mécanisme de création de refs pour les composants de classe, offrant une API orientée objet qui s'intègre naturellement dans la structure de la classe.
Syntaxe et utilisation de base : un processus en trois étapes
Le flux de travail pour utiliser createRef() est simple et implique trois étapes clés :
-
Créer un objet Ref : Dans le constructeur de votre composant de classe, initialisez une instance de ref en appelant
React.createRef()et en assignant sa valeur de retour à une propriété d'instance (par exemple,this.myRef). -
Attacher la Ref : Dans la méthode
renderde votre composant, passez l'objet ref créé à l'attributrefde l'élément React (soit un élément HTML, soit un composant de classe) que vous souhaitez référencer. -
Accéder à la cible : Une fois le composant monté, le nœud DOM ou l'instance de composant référencé sera disponible via la propriété
.currentde votre objet ref (par exemple,this.myRef.current).
import React from 'react';
class FocusInputOnMount extends React.Component {
constructor(props) {
super(props);
this.inputElementRef = React.createRef(); // Étape 1 : Créer un objet ref dans le constructeur
console.log('Constructeur : La valeur actuelle de la Ref est initialement :', this.inputElementRef.current); // null
}
componentDidMount() {
if (this.inputElementRef.current) {
this.inputElementRef.current.focus();
console.log('ComponentDidMount : Input a le focus. Valeur actuelle :', this.inputElementRef.current.value);
}
}
handleButtonClick = () => {
if (this.inputElementRef.current) {
alert(`Valeur de l'input : ${this.inputElementRef.current.value}`);
}
};
render() {
console.log('Render : La valeur actuelle de la Ref est :', this.inputElementRef.current); // Toujours null lors du rendu initial
return (
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
<h3>Champ de saisie avec focus automatique</h3>
<label htmlFor="focusInput">Entrez votre nom :</label><br />
<input
id="focusInput"
type="text"
ref={this.inputElementRef} // Étape 2 : Attacher la ref à l'élément <input>
placeholder="Votre nom ici..."
style={{ margin: '10px 0', padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
/><br />
<button
onClick={this.handleButtonClick}
style={{ padding: '10px 15px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Afficher la valeur de l'input
</button>
<p><em>Ce champ de saisie recevra automatiquement le focus lors du chargement du composant.</em></p>
</div>
);
}
}
Dans cet exemple, this.inputElementRef est un objet que React gérera en interne. Lorsque l'élément <input> est rendu et monté dans le DOM, React assigne ce nœud DOM réel à this.inputElementRef.current. La méthode de cycle de vie componentDidMount est l'endroit idéal pour interagir avec les refs car elle garantit que le composant et ses enfants ont été rendus dans le DOM et que la propriété .current est disponible et renseignée.
Attacher une Ref à un élément du DOM : accès direct au DOM
Lorsque vous attachez une ref à un élément HTML standard (par exemple, <div>, <p>, <button>, <img>), la propriété .current de votre objet ref contiendra l'élément DOM sous-jacent réel. Cela vous donne un accès illimité à toutes les API DOM standard du navigateur, vous permettant d'effectuer des actions qui sont généralement en dehors du contrôle déclaratif de React. Ceci est particulièrement utile pour les applications globales où une mise en page, un défilement ou une gestion du focus précis peuvent être critiques dans divers environnements utilisateur et types d'appareils.
import React from 'react';
class ScrollToElementExample extends React.Component {
constructor(props) {
super(props);
this.targetDivRef = React.createRef();
this.state = { showScrollButton: false };
}
componentDidMount() {
// Afficher le bouton de défilement uniquement s'il y a assez de contenu pour défiler
// Cette vérification garantit également que la ref est déjà actuelle.
if (this.targetDivRef.current && window.innerHeight < document.body.scrollHeight) {
this.setState({ showScrollButton: true });
}
}
handleScrollToTarget = () => {
if (this.targetDivRef.current) {
// Utilisation de scrollIntoView pour un défilement fluide, largement pris en charge par les navigateurs du monde entier.
this.targetDivRef.current.scrollIntoView({
behavior: 'smooth', // Anime le défilement pour une meilleure expérience utilisateur
block: 'start' // Aligne le haut de l'élément en haut de la fenêtre d'affichage
});
console.log('Défilement vers la div cible !');
} else {
console.warn('La div cible n\'est pas encore disponible pour le défilement.');
}
};
render() {
return (
<div style={{ padding: '15px' }}>
<h2>Défilement vers un élément spécifique avec une Ref</h2>
<p>Cet exemple montre comment faire défiler par programmation jusqu'à un élément du DOM qui est hors de l'écran.</p>
{this.state.showScrollButton && (
<button
onClick={this.handleScrollToTarget}
style={{ marginBottom: '20px', padding: '10px 20px', background: '#28a745', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Faire défiler jusqu'à la zone cible
</button>
)}
<div style={{ height: '1500px', background: '#f8f9fa', padding: '20px', marginBottom: '20px', border: '1px dashed #6c757d' }}>
<p>Contenu de remplissage pour créer un espace de défilement vertical.</p>
<p>Imaginez de longs articles, des formulaires complexes ou des tableaux de bord détaillés qui obligent les utilisateurs à naviguer dans un contenu étendu. Le défilement par programmation garantit que les utilisateurs peuvent atteindre rapidement les sections pertinentes sans effort manuel, améliorant ainsi l'accessibilité et le flux utilisateur sur tous les appareils et toutes les tailles d'écran.</p>
<p>Cette technique est particulièrement utile dans les formulaires multi-pages, les assistants pas à pas ou les applications monopages avec une navigation profonde.</p>
</div>
<div
ref={this.targetDivRef} // Attachez la ref ici
style={{
minHeight: '300px',
background: '#e9ecef',
padding: '30px',
border: '2px solid #007bff',
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center'
}}
>
<h3>Vous avez atteint la zone cible !</h3>
<p>Ceci est la section vers laquelle nous avons défilé par programmation.</p>
<p>La capacité de contrôler précisément le comportement de défilement est cruciale pour améliorer l'expérience utilisateur, en particulier sur les appareils mobiles où l'espace d'écran est limité et une navigation précise est primordiale.</p>
</div>
</div>
);
}
}
Cet exemple illustre magnifiquement comment createRef fournit un contrôle sur les interactions au niveau du navigateur. De telles capacités de défilement par programmation sont essentielles dans de nombreuses applications, de la navigation dans une longue documentation au guidage des utilisateurs à travers des flux de travail complexes. L'option behavior: 'smooth' dans scrollIntoView assure une transition agréable et animée, améliorant l'expérience utilisateur de manière universelle.
Attacher une Ref à un composant de classe : interagir avec les instances
Au-delà des éléments DOM natifs, vous pouvez également attacher une ref à une instance d'un composant de classe. Lorsque vous faites cela, la propriété .current de votre objet ref contiendra le composant de classe instancié réel lui-même. Cela permet à un composant parent d'appeler directement des méthodes définies dans le composant de classe enfant ou d'accéder à ses propriétés d'instance. Bien que puissante, cette capacité doit être utilisée avec une extrême prudence, car elle permet de rompre le flux de données unidirectionnel traditionnel, ce qui peut conduire à un comportement d'application moins prévisible.
import React from 'react';
// Composant de classe enfant
class DialogBox extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false, message: '' };
}
// Méthode exposée au parent via la ref
open(message) {
this.setState({ isOpen: true, message });
}
close = () => {
this.setState({ isOpen: false, message: '' });
};
render() {
if (!this.state.isOpen) return null;
return (
<div style={{
position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
padding: '25px 35px', background: 'white', border: '1px solid #ddd', borderRadius: '8px',
boxShadow: '0 5px 15px rgba(0,0,0,0.2)', zIndex: 1000, maxWidth: '400px', width: '90%', textAlign: 'center'
}}>
<h4>Message du Parent</h4>
<p>{this.state.message}</p>
<button
onClick={this.close}
style={{ marginTop: '15px', padding: '8px 15px', background: '#dc3545', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Fermer
</button>
</div>
);
}
}
// Composant de classe parent
class AppWithDialog extends React.Component {
constructor(props) {
super(props);
this.dialogRef = React.createRef();
}
handleOpenDialog = () => {
if (this.dialogRef.current) {
// Accéder à l'instance du composant enfant et appeler sa méthode 'open'
this.dialogRef.current.open('Bonjour du composant parent ! Cette boîte de dialogue a été ouverte de manière impérative.');
}
};
render() {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Communication Parent-Enfant via Ref</h2>
<p>Ceci démontre comment un composant parent peut contrôler impérativement une méthode de son composant de classe enfant.</p>
<button
onClick={this.handleOpenDialog}
style={{ padding: '12px 25px', background: '#007bff', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '1.1em' }}
>
Ouvrir la boîte de dialogue impérative
</button>
<DialogBox ref={this.dialogRef} /> // Attacher la ref à une instance de composant de classe
</div>
);
}
}
Ici, AppWithDialog peut invoquer directement la méthode open du composant DialogBox via sa ref. Ce modèle peut être utile pour déclencher des actions comme l'affichage d'une modale, la réinitialisation d'un formulaire, ou le contrôle programmatique d'éléments d'interface utilisateur externes encapsulés dans un composant enfant. Cependant, il est généralement recommandé de privilégier la communication basée sur les props pour la plupart des scénarios, en passant les données et les rappels du parent à l'enfant pour maintenir un flux de données clair et prévisible. Ne recourez aux refs pour les méthodes de composants enfants que lorsque ces actions sont véritablement impératives et ne correspondent pas au flux typique prop/état.
Attacher une Ref à un composant fonctionnel (une distinction cruciale)
C'est une idée fausse courante, et un point de distinction important, que vous ne pouvez pas attacher directement une ref en utilisant createRef() à un composant fonctionnel. Les composants fonctionnels, de par leur nature, n'ont pas d'instances de la même manière que les composants de classe. Si vous tentez d'assigner une ref directement à un composant fonctionnel (par exemple, <MyFunctionalComponent ref={this.myRef} />), React émettra un avertissement en mode développement car il n'y a pas d'instance de composant à assigner à .current.
Si votre objectif est de permettre à un composant parent (qui peut être un composant de classe utilisant createRef, ou un composant fonctionnel utilisant useRef) d'accéder à un élément DOM rendu à l'intérieur d'un composant enfant fonctionnel, vous devez utiliser React.forwardRef. Ce composant d'ordre supérieur permet aux composants fonctionnels d'exposer une ref à un nœud DOM spécifique ou à une poignée impérative en leur sein.
Alternativement, si vous travaillez à l'intérieur d'un composant fonctionnel et que vous avez besoin de créer et de gérer une ref, le mécanisme approprié est le hook useRef, qui sera brièvement discuté dans une section de comparaison ultérieure. Il est vital de se rappeler que createRef est fondamentalement lié aux composants de classe et à leur nature basée sur les instances.
Accéder au nœud DOM ou à l'instance du composant : la propriété `.current` expliquée
Le cœur de l'interaction avec les refs tourne autour de la propriété .current de l'objet ref créé par React.createRef(). Comprendre son cycle de vie et ce qu'elle peut contenir est primordial pour une gestion efficace des refs.
La propriété `.current` : votre passerelle vers le contrôle impératif
La propriété .current est un objet mutable que React gère. Elle sert de lien direct vers l'élément ou l'instance de composant référencé. Sa valeur change tout au long du cycle de vie du composant :
-
Initialisation : Lorsque vous appelez pour la première fois
React.createRef()dans le constructeur, l'objet ref est créé, et sa propriété.currentest initialisée ànull. C'est parce qu'à ce stade, le composant n'a pas encore été rendu, et aucun élément DOM ou instance de composant n'existe pour que la ref puisse y pointer. -
Montage : Une fois que le composant est rendu dans le DOM et que l'élément avec l'attribut
refest créé, React assigne le nœud DOM réel ou l'instance du composant de classe à la propriété.currentde votre objet ref. Cela se produit généralement immédiatement après la fin de la méthoderenderet avant quecomponentDidMountne soit appelé. Par conséquent,componentDidMountest l'endroit le plus sûr et le plus courant pour accéder et interagir avec.current. -
Démontage : Lorsque le composant est démonté du DOM, React réinitialise automatiquement la propriété
.currentànull. Ceci est crucial pour prévenir les fuites de mémoire et s'assurer que votre application ne conserve pas de références à des éléments qui n'existent plus dans le DOM. -
Mise à jour : Dans les rares cas où l'attribut
refest modifié sur un élément lors d'une mise à jour, la propriétécurrentde l'ancienne ref sera mise ànullavant que la propriétécurrentde la nouvelle ref ne soit définie. Ce comportement est moins courant mais important à noter pour les assignations de refs dynamiques complexes.
import React from 'react';
class RefLifecycleLogger extends React.Component {
constructor(props) {
super(props);
this.myDivRef = React.createRef();
console.log('1. Constructeur : this.myDivRef.current est', this.myDivRef.current); // null
}
componentDidMount() {
console.log('3. componentDidMount : this.myDivRef.current est', this.myDivRef.current); // L'élément DOM réel
if (this.myDivRef.current) {
this.myDivRef.current.style.backgroundColor = '#d4edda'; // Style impératif pour la démonstration
this.myDivRef.current.innerText += ' - La Ref est active !';
}
}
componentDidUpdate(prevProps, prevState) {
console.log('4. componentDidUpdate : this.myDivRef.current est', this.myDivRef.current); // L'élément DOM réel (après les mises à jour)
}
componentWillUnmount() {
console.log('5. componentWillUnmount : this.myDivRef.current est', this.myDivRef.current); // L'élément DOM réel (juste avant d'être mis à null)
// À ce stade, vous pourriez effectuer un nettoyage si nécessaire
}
render() {
// Lors du rendu initial, this.myDivRef.current est toujours null car le DOM n'a pas encore été créé.
// Lors des rendus suivants (après le montage), il contiendra l'élément.
console.log('2. Render : this.myDivRef.current est', this.myDivRef.current);
return (
<div
ref={this.myDivRef}
style={{ padding: '20px', border: '1px solid #28a745', margin: '20px', minHeight: '80px', display: 'flex', alignItems: 'center' }}
>
<p>Ceci est une div à laquelle une ref est attachée.</p>
</div>
);
}
}
Observer la sortie de la console pour RefLifecycleLogger donne un aperçu clair du moment où this.myDivRef.current devient disponible. Il est crucial de toujours vérifier si this.myDivRef.current n'est pas null avant de tenter d'interagir avec, en particulier dans les méthodes qui pourraient s'exécuter avant le montage ou après le démontage.
Que peut contenir `.current` ? Explorer le contenu de votre Ref
Le type de valeur que current contient dépend de ce à quoi vous attachez la ref :
-
Lorsqu'elle est attachée à un élément HTML (par exemple,
<div>,<input>) : La propriété.currentcontiendra l'élément DOM sous-jacent réel. Il s'agit d'un objet JavaScript natif, donnant accès à sa gamme complète d'API DOM. Par exemple, si vous attachez une ref à un<input type="text">,.currentsera un objetHTMLInputElement, vous permettant d'appeler des méthodes comme.focus(), de lire des propriétés comme.value, ou de modifier des attributs comme.placeholder. C'est le cas d'utilisation le plus courant pour les refs.this.inputRef.current.focus();
this.videoRef.current.play();
const { width, height } = this.divRef.current.getBoundingClientRect(); -
Lorsqu'elle est attachée à un composant de classe (par exemple,
<MyClassComponent />) : La propriété.currentcontiendra l'instance de ce composant de classe. Cela signifie que vous pouvez appeler directement des méthodes définies dans ce composant enfant (par exemple,childRef.current.someMethod()) ou même accéder à son état ou à ses props (bien que l'accès direct à l'état/aux props d'un enfant via une ref soit généralement déconseillé en faveur des mises à jour des props et de l'état). Cette capacité est puissante pour déclencher des comportements spécifiques dans les composants enfants qui ne correspondent pas au modèle d'interaction standard basé sur les props.this.childComponentRef.current.resetForm();
// Rarement, mais possible : console.log(this.childComponentRef.current.state.someValue); -
Lorsqu'elle est attachée à un composant fonctionnel (via
forwardRef) : Comme indiqué précédemment, les refs ne peuvent pas être attachées directement aux composants fonctionnels. Cependant, si un composant fonctionnel est enveloppé avecReact.forwardRef, alors la propriété.currentcontiendra la valeur que le composant fonctionnel expose explicitement via la ref transmise. Il s'agit généralement d'un élément DOM au sein du composant fonctionnel, ou d'un objet contenant des méthodes impératives (en utilisant le hookuseImperativeHandleen conjonction avecforwardRef).// Dans le parent, myForwardedRef.current serait le nœud DOM ou l'objet exposé
this.myForwardedRef.current.focus();
this.myForwardedRef.current.customResetMethod();
Cas d'utilisation pratiques de `createRef` en action
Pour vraiment saisir l'utilité de React.createRef(), explorons des scénarios plus détaillés et pertinents à l'échelle mondiale où il s'avère indispensable, allant au-delà de la simple gestion du focus.
1. Gérer le focus, la sélection de texte ou la lecture multimédia à travers les cultures
Ce sont des exemples de premier ordre d'interactions d'interface utilisateur impératives. Imaginez un formulaire en plusieurs étapes conçu pour un public mondial. Après qu'un utilisateur a terminé une section, vous pourriez vouloir déplacer automatiquement le focus vers le premier champ de la section suivante, indépendamment de la langue ou de la direction du texte par défaut (de gauche à droite ou de droite à gauche). Les refs fournissent le contrôle nécessaire.
import React from 'react';
class DynamicFocusForm extends React.Component {
constructor(props) {
super(props);
this.firstNameRef = React.createRef();
this.lastNameRef = React.createRef();
this.emailRef = React.createRef();
this.state = { currentStep: 1 };
}
componentDidMount() {
// Mettre le focus sur le premier champ lorsque le composant est monté
this.firstNameRef.current.focus();
}
handleNextStep = (nextRef) => {
this.setState(prevState => ({ currentStep: prevState.currentStep + 1 }), () => {
// Après la mise à jour de l'état et le nouveau rendu du composant, mettre le focus sur le champ suivant
if (nextRef.current) {
nextRef.current.focus();
}
});
};
render() {
const { currentStep } = this.state;
const formSectionStyle = { border: '1px solid #0056b3', padding: '20px', margin: '15px 0', borderRadius: '8px', background: '#e7f0fa' };
const inputStyle = { width: '100%', padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px' };
const buttonStyle = { padding: '10px 20px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginTop: '10px' };
return (
<div style={{ maxWidth: '600px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', borderRadius: '10px', background: 'white' }}>
<h2>Formulaire multi-étapes avec gestion du focus par Ref</h2>
<p>Étape actuelle : <strong>{currentStep}</strong></p>
{currentStep === 1 && (
<div style={formSectionStyle}>
<h3>Détails personnels</h3>
<label htmlFor="firstName">Prénom :</label>
<input id="firstName" type="text" ref={this.firstNameRef} style={inputStyle} placeholder="ex: Jean" />
<label htmlFor="lastName">Nom :</label>
<input id="lastName" type="text" ref={this.lastNameRef} style={inputStyle} placeholder="ex: Dupont" />
<button onClick={() => this.handleNextStep(this.emailRef)} style={buttonStyle}>Suivant →</button>
</div>
)}
{currentStep === 2 && (
<div style={formSectionStyle}>
<h3>Coordonnées</h3>
<label htmlFor="email">Email :</label>
<input id="email" type="email" ref={this.emailRef} style={inputStyle} placeholder="ex: jean.dupont@example.com" />
<p>... autres champs de contact ...</p>
<button onClick={() => alert('Formulaire soumis !')} style={buttonStyle}>Soumettre</button>
</div>
)}
<p><em>Cette interaction améliore considérablement l'accessibilité et l'expérience utilisateur, en particulier pour les utilisateurs qui dépendent de la navigation au clavier ou des technologies d'assistance dans le monde entier.</em></p>
</div>
);
}
}
Cet exemple présente un formulaire pratique en plusieurs étapes où createRef est utilisé pour gérer le focus par programmation. Cela garantit un parcours utilisateur fluide et accessible, une considération essentielle pour les applications utilisées dans divers contextes linguistiques et culturels. De même, pour les lecteurs multimédias, les refs vous permettent de créer des contrôles personnalisés (lecture, pause, volume, recherche) qui interagissent directement avec les API natives des éléments HTML5 <video> ou <audio>, offrant une expérience cohérente indépendamment des paramètres par défaut du navigateur.
2. Déclencher des animations impératives et des interactions Canvas
Bien que les bibliothèques d'animation déclaratives soient excellentes pour de nombreux effets d'interface utilisateur, certaines animations avancées, en particulier celles qui exploitent l'API HTML5 Canvas, WebGL, ou qui nécessitent un contrôle fin des propriétés des éléments qui sont mieux gérées en dehors du cycle de rendu de React, bénéficient grandement des refs. Par exemple, la création d'une visualisation de données en temps réel ou d'un jeu sur un élément Canvas implique de dessiner directement sur un tampon de pixels, un processus intrinsèquement impératif.
import React from 'react';
class CanvasAnimator extends React.Component {
constructor(props) {
super(props);
this.canvasRef = React.createRef();
this.animationFrameId = null;
}
componentDidMount() {
this.startAnimation();
}
componentWillUnmount() {
this.stopAnimation();
}
startAnimation = () => {
const canvas = this.canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let angle = 0;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 50;
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Effacer le canvas
// Dessiner un carré en rotation
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(angle);
ctx.fillStyle = '#6f42c1';
ctx.fillRect(-radius / 2, -radius / 2, radius, radius);
ctx.restore();
angle += 0.05; // Incrémenter l'angle pour la rotation
this.animationFrameId = requestAnimationFrame(animate);
};
this.animationFrameId = requestAnimationFrame(animate);
};
stopAnimation = () => {
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
}
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #ced4da', padding: '20px', borderRadius: '8px', background: '#f8f9fa' }}>
<h3>Animation Canvas impérative avec createRef</h3>
<p>Cette animation canvas est contrôlée directement à l'aide des API du navigateur via une ref.</p>
<canvas ref={this.canvasRef} width="300" height="200" style={{ border: '1px solid #adb5bd', background: 'white' }}>
Votre navigateur ne prend pas en charge la balise canvas HTML5.
</canvas>
<p><em>Un tel contrôle direct est vital pour les graphiques haute performance, les jeux ou les visualisations de données spécialisées utilisées dans diverses industries à l'échelle mondiale.</em></p>
</div>
);
}
}
Ce composant fournit un élément canvas et utilise une ref pour obtenir un accès direct à son contexte de rendu 2D. La boucle d'animation, alimentée par `requestAnimationFrame`, dessine et met à jour de manière impérative un carré en rotation. Ce modèle est fondamental pour la construction de tableaux de bord de données interactifs, d'outils de conception en ligne, ou même de jeux occasionnels qui exigent un rendu précis, image par image, indépendamment de la situation géographique de l'utilisateur ou des capacités de son appareil.
3. Intégration avec des bibliothèques DOM tierces : un pont transparent
L'une des raisons les plus convaincantes d'utiliser les refs est d'intégrer React avec des bibliothèques JavaScript externes qui manipulent directement le DOM. De nombreuses bibliothèques puissantes, en particulier les plus anciennes ou celles axées sur des tâches de rendu spécifiques (comme les graphiques, la cartographie ou l'édition de texte enrichi), fonctionnent en prenant un élément DOM comme cible et en gérant ensuite elles-mêmes son contenu. React, dans son mode déclaratif, entrerait autrement en conflit avec ces bibliothèques en tentant de contrôler le même sous-arbre DOM. Les refs préviennent ce conflit en fournissant un 'conteneur' désigné pour la bibliothèque externe.
import React from 'react';
import * as d3 from 'd3'; // En supposant que D3.js est installé et importé
class D3BarChart extends React.Component {
constructor(props) {
super(props);
this.chartContainerRef = React.createRef();
}
// Lorsque le composant est monté, dessiner le graphique
componentDidMount() {
this.drawChart();
}
// Lorsque le composant est mis à jour (par ex., props.data change), mettre à jour le graphique
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.drawChart();
}
}
// Lorsque le composant est démonté, nettoyer les éléments D3 pour éviter les fuites de mémoire
componentWillUnmount() {
d3.select(this.chartContainerRef.current).selectAll('*').remove();
}
drawChart = () => {
const data = this.props.data || [40, 80, 20, 100, 60, 90]; // Données par défaut
const node = this.chartContainerRef.current;
if (!node) return; // S'assurer que la ref est disponible
// Effacer tous les éléments de graphique précédents dessinés par D3
d3.select(node).selectAll('*').remove();
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const width = 460 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select(node)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// Mettre en place les échelles
const x = d3.scaleBand()
.range([0, width])
.padding(0.1);
const y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map((d, i) => i)); // Utiliser l'index comme domaine pour la simplicité
y.domain([0, d3.max(data)]);
// Ajouter les barres
svg.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => x(i))
.attr('width', x.bandwidth())
.attr('y', d => y(d))
.attr('height', d => height - y(d))
.attr('fill', '#17a2b8');
// Ajouter l'axe X
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
// Ajouter l'axe Y
svg.append('g')
.call(d3.axisLeft(y));
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #00a0b2', padding: '20px', borderRadius: '8px', background: '#e0f7fa' }}>
<h3>Intégration de graphiques D3.js avec React createRef</h3>
<p>Cette visualisation de données est rendue par D3.js dans un conteneur géré par React.</p>
<div ref={this.chartContainerRef} /> // D3.js effectuera son rendu dans cette div
<p><em>L'intégration de telles bibliothèques spécialisées est cruciale pour les applications riches en données, fournissant des outils d'analyse puissants aux utilisateurs de divers secteurs et régions.</em></p>
</div>
);
}
}
Cet exemple détaillé montre l'intégration d'un diagramme à barres D3.js au sein d'un composant de classe React. Le chartContainerRef fournit à D3.js le nœud DOM spécifique dont il a besoin pour effectuer son rendu. React gère le cycle de vie du conteneur <div>, tandis que D3.js gère son contenu interne. Les méthodes `componentDidUpdate` et `componentWillUnmount` sont vitales pour mettre à jour le graphique lorsque les données changent et pour effectuer le nettoyage nécessaire, évitant les fuites de mémoire et garantissant une expérience réactive. Ce modèle est universellement applicable, permettant aux développeurs de tirer parti du meilleur du modèle de composant de React et des bibliothèques de visualisation spécialisées et performantes pour les tableaux de bord et les plateformes d'analyse mondiales.
4. Mesurer les dimensions ou la position d'un élément pour des mises en page dynamiques
Pour des mises en page très dynamiques ou réactives, ou pour implémenter des fonctionnalités comme des listes virtualisées qui ne rendent que les éléments visibles, il est essentiel de connaître les dimensions et la position précises des éléments. Les refs vous permettent d'accéder à la méthode getBoundingClientRect(), qui fournit ces informations cruciales directement depuis le DOM.
import React from 'react';
class ElementDimensionLogger extends React.Component {
constructor(props) {
super(props);
this.measurableDivRef = React.createRef();
this.state = {
width: 0,
height: 0,
top: 0,
left: 0,
message: 'Cliquez sur le bouton pour mesurer !'
};
}
componentDidMount() {
// Une mesure initiale est souvent utile, mais peut aussi être déclenchée par une action de l'utilisateur
this.measureElement();
// Pour les mises en page dynamiques, vous pourriez écouter les événements de redimensionnement de la fenêtre
window.addEventListener('resize', this.measureElement);
}
componentWillUnmount() {
window.removeEventListener('resize', this.measureElement);
}
measureElement = () => {
if (this.measurableDivRef.current) {
const rect = this.measurableDivRef.current.getBoundingClientRect();
this.setState({
width: Math.round(rect.width),
height: Math.round(rect.height),
top: Math.round(rect.top),
left: Math.round(rect.left),
message: 'Dimensions mises à jour.'
});
} else {
this.setState({ message: 'Élément pas encore rendu.' });
}
};
render() {
const { width, height, top, left, message } = this.state;
const boxStyle = {
width: '70%',
minHeight: '150px',
border: '3px solid #ffc107',
margin: '25px auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
background: '#fff3cd',
borderRadius: '8px',
textAlign: 'center'
};
return (
<div style={{ maxWidth: '700px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.08)', borderRadius: '10px', background: 'white' }}>
<h3>Mesure des dimensions d'un élément avec createRef</h3>
<p>Cet exemple récupère et affiche dynamiquement la taille et la position d'un élément cible.</p>
<div ref={this.measurableDivRef} style={boxStyle}>
<p><strong>Je suis l'élément en cours de mesure.</strong></p>
<p>Redimensionnez la fenêtre de votre navigateur pour voir les mesures changer lors du rafraîchissement/déclenchement manuel.</p>
</div>
<button
onClick={this.measureElement}
style={{ padding: '10px 20px', background: '#6c757d', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginBottom: '15px' }}
>
Mesurer maintenant
</button>
<div style={{ background: '#f0f0f0', padding: '15px', borderRadius: '6px' }}>
<p><strong>Dimensions en direct :</strong></p>
<ul style={{ listStyleType: 'none', padding: 0, textAlign: 'left', margin: '0 auto', maxWidth: '300px' }}>
<li>Largeur : <b>{width}px</b></li>
<li>Hauteur : <b>{height}px</b></li>
<li>Position haute (Viewport) : <b>{top}px</b></li>
<li>Position gauche (Viewport) : <b>{left}px</b></li>
</ul>
<p><em>La mesure précise des éléments est essentielle pour les conceptions réactives et l'optimisation des performances sur divers appareils à l'échelle mondiale.</em></p>
</div>
</div>
);
}
}
Ce composant utilise createRef pour obtenir le getBoundingClientRect() d'un élément div, fournissant ses dimensions et sa position en temps réel. Cette information est inestimable pour implémenter des ajustements de mise en page complexes, déterminer la visibilité dans une liste à défilement virtualisée, ou même pour s'assurer que les éléments se trouvent dans une zone de viewport spécifique. Pour un public mondial, où les tailles d'écran, les résolutions et les environnements de navigateur varient considérablement, un contrôle précis de la mise en page basé sur des mesures réelles du DOM est un facteur clé pour offrir une expérience utilisateur cohérente et de haute qualité.
Bonnes pratiques et mises en garde pour l'utilisation de `createRef`
Bien que createRef offre un contrôle impératif puissant, son mauvais usage peut conduire à un code difficile à gérer et à déboguer. Le respect des bonnes pratiques est essentiel pour exploiter sa puissance de manière responsable.
1. Prioriser les approches déclaratives : la règle d'or
Rappelez-vous toujours que les refs sont une "échappatoire", pas le mode d'interaction principal dans React. Avant de vous tourner vers une ref, demandez-vous : cela peut-il être réalisé avec l'état et les props ? Si la réponse est oui, alors c'est presque toujours la meilleure approche, plus "idiomatique à React". Par exemple, si vous voulez changer la valeur d'un champ de saisie, utilisez des composants contrôlés avec l'état, pas une ref pour définir directement inputRef.current.value.
2. Les Refs sont pour les interactions impératives, pas pour la gestion de l'état
Les refs sont les mieux adaptées aux tâches qui impliquent des actions directes et impératives sur les éléments DOM ou les instances de composants. Ce sont des commandes : "mettre le focus sur cet input", "lire cette vidéo", "défiler jusqu'à cette section". Elles ne sont pas destinées à changer l'interface utilisateur déclarative d'un composant en fonction de l'état. La manipulation directe du style ou du contenu d'un élément via une ref alors que cela pourrait être contrôlé par des props ou l'état peut entraîner une désynchronisation entre le DOM virtuel de React et le DOM réel, provoquant un comportement imprévisible et des problèmes de rendu.
3. Refs et composants fonctionnels : adoptez `useRef` et `forwardRef`
Pour le développement React moderne au sein de composants fonctionnels, React.createRef() n'est pas l'outil que vous utiliserez. À la place, vous vous appuierez sur le hook useRef. Le hook useRef fournit un objet ref mutable similaire à createRef, dont la propriété .current peut être utilisée pour les mêmes interactions impératives. Il conserve sa valeur entre les re-rendus du composant sans provoquer lui-même de re-rendu, ce qui le rend parfait pour conserver une référence à un nœud DOM ou à toute valeur mutable qui doit persister entre les rendus.
import React, { useRef, useEffect } from 'react';
function FunctionalComponentWithRef() {
const myInputRef = useRef(null); // Initialiser avec null
useEffect(() => {
// Ceci s'exécute après le montage du composant
if (myInputRef.current) {
myInputRef.current.focus();
console.log('Input du composant fonctionnel a le focus !');
}
}, []); // Le tableau de dépendances vide garantit qu'il ne s'exécute qu'une seule fois au montage
const handleLogValue = () => {
if (myInputRef.current) {
alert(`Valeur de l'input : ${myInputRef.current.value}`);
}
};
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #009688', borderRadius: '8px', background: '#e0f2f1' }}>
<h3>Utilisation de useRef dans un composant fonctionnel</h3>
<label htmlFor="funcInput">Écrivez quelque chose :</label><br />
<input id="funcInput" type="text" ref={myInputRef} placeholder="J'ai le focus automatiquement !" style={{ padding: '8px', margin: '10px 0', borderRadius: '4px', border: '1px solid #ccc' }} /><br />
<button onClick={handleLogValue} style={{ padding: '10px 15px', background: '#009688', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}>
Afficher la valeur de l'input
</button>
<p><em>Pour les nouveaux projets, `useRef` est le choix idiomatique pour les refs dans les composants fonctionnels.</em></p>
</div>
);
}
Si vous avez besoin qu'un composant parent obtienne une ref vers un élément DOM à l'intérieur d'un composant enfant fonctionnel, alors React.forwardRef est votre solution. C'est un composant d'ordre supérieur qui vous permet de "transférer" une ref d'un parent à l'un des éléments DOM de ses enfants, maintenant l'encapsulation du composant fonctionnel tout en permettant un accès impératif lorsque nécessaire.
import React, { useRef, useEffect } from 'react';
// Composant fonctionnel qui transfère explicitement une ref à son élément input natif
const ForwardedInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} className="forwarded-input" placeholder={props.placeholder} style={{ padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px', width: '100%' }} />
));
class ParentComponentUsingForwardRef extends React.Component {
constructor(props) {
super(props);
this.parentInputRef = React.createRef();
}
componentDidMount() {
if (this.parentInputRef.current) {
this.parentInputRef.current.focus();
console.log('Input dans le composant fonctionnel a le focus depuis le parent (composant de classe) via une ref transférée !');
}
}
render() {
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #6f42c1', borderRadius: '8px', background: '#f5eef9' }}>
<h3>Exemple de transfert de Ref avec createRef (Composant de classe parent)</h3>
<label>Entrez les détails :</label>
<ForwardedInput ref={this.parentInputRef} placeholder="Cet input est dans un composant fonctionnel" />
<p><em>Ce modèle est crucial pour créer des bibliothèques de composants réutilisables qui doivent exposer un accès direct au DOM.</em></p>
</div>
);
}
}
Ceci démontre comment un composant de classe utilisant createRef peut interagir efficacement avec un élément DOM imbriqué dans un composant fonctionnel en tirant parti de forwardRef. Cela rend les composants fonctionnels tout aussi capables de participer à des interactions impératives lorsque cela est nécessaire, garantissant que les bases de code React modernes peuvent toujours bénéficier des refs.
4. Quand ne pas utiliser les Refs : maintenir l'intégrité de React
- Pour contrôler l'état d'un composant enfant : N'utilisez jamais une ref pour lire ou mettre à jour directement l'état d'un composant enfant. Cela contourne la gestion de l'état de React, rendant votre application imprévisible. Au lieu de cela, passez l'état en tant que props, et utilisez des rappels pour permettre aux enfants de demander des changements d'état aux parents.
- En remplacement des props : Bien que vous puissiez appeler des méthodes sur un composant de classe enfant via une ref, demandez-vous si passer un gestionnaire d'événements en tant que prop à l'enfant n'atteindrait pas le même objectif d'une manière plus "idiomatique à React". Les props favorisent un flux de données clair et rendent les interactions entre composants transparentes.
-
Pour de simples manipulations du DOM que React peut gérer : Si vous voulez changer le texte, le style, ou ajouter/supprimer une classe d'un élément en fonction de l'état, faites-le de manière déclarative. Par exemple, pour basculer une classe
active, appliquez-la conditionnellement en JSX :<div className={isActive ? 'active' : ''}>, plutôt quedivRef.current.classList.add('active').
5. Considérations sur la performance et portée globale
Bien que createRef lui-même soit performant, les opérations effectuées avec current peuvent avoir des implications significatives sur la performance. Pour les utilisateurs sur des appareils bas de gamme ou avec des connexions réseau plus lentes (courant dans de nombreuses parties du monde), des manipulations DOM inefficaces peuvent entraîner des saccades, des interfaces utilisateur non réactives et une mauvaise expérience utilisateur. Lors de l'utilisation de refs pour des tâches comme les animations, les calculs de mise en page complexes ou l'intégration de lourdes bibliothèques tierces :
-
Debounce/Throttle les événements : Si vous utilisez des refs pour mesurer des dimensions sur les événements
window.resizeouscroll, assurez-vous que ces gestionnaires sont "debounced" ou "throttled" pour éviter des appels de fonction excessifs et des lectures du DOM. -
Grouper les lectures/écritures du DOM : Évitez d'entrelacer les opérations de lecture du DOM (par exemple,
getBoundingClientRect()) avec les opérations d'écriture du DOM (par exemple, la définition de styles). Cela peut déclencher un "layout thrashing". Des outils commefastdompeuvent aider à gérer cela efficacement. -
Différer les opérations non critiques : Utilisez
requestAnimationFramepour les animations etsetTimeout(..., 0)ourequestIdleCallbackpour les manipulations DOM moins critiques afin de garantir qu'elles ne bloquent pas le thread principal et n'impactent pas la réactivité. - Choisir judicieusement : Parfois, la performance d'une bibliothèque tierce peut être un goulot d'étranglement. Évaluez des alternatives ou envisagez de charger paresseusement de tels composants pour les utilisateurs sur des connexions plus lentes, garantissant qu'une expérience de base reste performante globalement.
`createRef` vs Refs par rappel vs `useRef` : une comparaison détaillée
React a offert différentes manières de gérer les refs tout au long de son évolution. Comprendre les nuances de chacune est essentiel pour choisir la méthode la plus appropriée à votre contexte spécifique.
1. `React.createRef()` (Composants de classe - Moderne)
-
Mécanisme : Crée un objet ref (
{ current: null }) dans le constructeur de l'instance du composant. React assigne l'élément DOM ou l'instance de composant à la propriété.currentaprès le montage. - Usage principal : Exclusivement au sein des composants de classe. Il est initialisé une fois par instance de composant.
-
Population de la Ref :
.currentest défini sur l'élément/l'instance après le montage du composant, et réinitialisé ànulllors du démontage. - Idéal pour : Toutes les exigences de ref standard dans les composants de classe où vous devez référencer un élément DOM ou une instance de composant de classe enfant.
- Avantages : Syntaxe claire et directe orientée objet. Pas de soucis de recréation de fonction en ligne causant des appels supplémentaires (comme cela peut arriver avec les refs par rappel).
- Inconvénients : Non utilisable avec les composants fonctionnels. S'il n'est pas initialisé dans le constructeur (par exemple, dans render), un nouvel objet ref pourrait être créé à chaque rendu, entraînant des problèmes de performance potentiels ou des valeurs de ref incorrectes. Nécessite de se souvenir de l'assigner à une propriété d'instance.
2. Refs par rappel (Composants de classe et fonctionnels - Flexible/Ancien)
-
Mécanisme : Vous passez une fonction directement à la prop
ref. React appelle cette fonction avec l'élément DOM monté ou l'instance de composant, et plus tard avecnulllorsqu'il est démonté. -
Usage principal : Peut être utilisé dans les composants de classe et fonctionnels. Dans les composants de classe, le rappel est généralement lié à
thisou défini comme une propriété de classe en fonction fléchée. Dans les composants fonctionnels, il est souvent défini en ligne ou mémoïsé. -
Population de la Ref : La fonction de rappel est invoquée directement par React. Vous êtes responsable du stockage de la référence (par exemple,
this.myInput = element;). -
Idéal pour : Les scénarios nécessitant un contrôle plus fin sur le moment où les refs sont définies et annulées, ou pour des modèles avancés comme les listes de refs dynamiques. C'était la principale façon de gérer les refs avant
createRefetuseRef. - Avantages : Offre une flexibilité maximale. Vous donne un accès immédiat à la ref lorsqu'elle est disponible (dans la fonction de rappel). Peut être utilisé pour stocker des refs dans un tableau ou une map pour des collections dynamiques d'éléments.
-
Inconvénients : Si le rappel est défini en ligne dans la méthode
render(par exemple,ref={el => this.myRef = el}), il sera appelé deux fois lors des mises à jour (une fois avecnull, puis avec l'élément), ce qui peut causer des problèmes de performance ou des effets secondaires inattendus s'il n'est pas géré avec soin (par exemple, en faisant du rappel une méthode de classe ou en utilisantuseCallbackdans les composants fonctionnels).
class CallbackRefDetailedExample extends React.Component {
constructor(props) {
super(props);
this.inputElement = null;
}
// Cette méthode sera appelée par React pour définir la ref
setInputElementRef = element => {
if (element) {
console.log('L\'élément de la ref est :', element);
}
this.inputElement = element; // Stocker l'élément DOM réel
};
componentDidMount() {
if (this.inputElement) {
this.inputElement.focus();
}
}
render() {
return (
<div>
<label>Input avec Ref par rappel :</label>
<input type="text" ref={this.setInputElementRef} />
</div>
);
}
}
3. Hook `useRef` (Composants fonctionnels - Moderne)
-
Mécanisme : Un Hook React qui renvoie un objet ref mutable (
{ current: initialValue }). L'objet renvoyé persiste pendant toute la durée de vie du composant fonctionnel. - Usage principal : Exclusivement au sein des composants fonctionnels.
-
Population de la Ref : Similaire à
createRef, React assigne l'élément DOM ou l'instance de composant (si transmise) à la propriété.currentaprès le montage et la met ànulllors du démontage. La valeur.currentpeut également être mise à jour manuellement. - Idéal pour : Toute la gestion des refs dans les composants fonctionnels. Également utile pour conserver toute valeur mutable qui doit persister entre les rendus sans déclencher un nouveau rendu (par exemple, les ID de minuterie, les valeurs précédentes).
- Avantages : Simple, idiomatique pour les Hooks. L'objet ref persiste entre les rendus, évitant les problèmes de recréation. Peut stocker n'importe quelle valeur mutable, pas seulement les nœuds DOM.
-
Inconvénients : Ne fonctionne qu'au sein des composants fonctionnels. Nécessite un
useEffectexplicite pour les interactions de ref liées au cycle de vie (comme la mise au point au montage).
En résumé :
-
Si vous écrivez un composant de classe et avez besoin d'une ref,
React.createRef()est le choix recommandé et le plus clair. -
Si vous écrivez un composant fonctionnel et avez besoin d'une ref, le Hook
useRefest la solution moderne et idiomatique. - Les refs par rappel sont toujours valides mais sont généralement plus verbeuses et sujettes à des problèmes subtils si elles ne sont pas implémentées avec soin. Elles sont utiles pour des scénarios avancés ou lorsque l'on travaille avec d'anciennes bases de code ou des contextes où les hooks ne sont pas disponibles.
-
Pour passer des refs à travers des composants (en particulier les fonctionnels),
React.forwardRef()est essentiel, souvent utilisé en conjonction aveccreateRefouuseRefdans le composant parent.
Considérations globales et accessibilité avancée avec les Refs
Bien que souvent discutée dans un vide technique, l'utilisation des refs dans un contexte d'application à vocation mondiale a des implications importantes, notamment en ce qui concerne la performance et l'accessibilité pour divers utilisateurs.
1. Optimisation des performances pour divers appareils et réseaux
L'impact de createRef lui-même sur la taille du bundle est minime, car il s'agit d'une petite partie du cœur de React. Cependant, les opérations que vous effectuez avec la propriété current peuvent avoir des implications significatives sur les performances. Pour les utilisateurs sur des appareils bas de gamme ou avec des connexions réseau plus lentes (courant dans de nombreuses parties du monde), des manipulations DOM inefficaces peuvent entraîner des saccades, des interfaces utilisateur non réactives et une mauvaise expérience utilisateur. Lors de l'utilisation de refs pour des tâches comme les animations, les calculs de mise en page complexes ou l'intégration de lourdes bibliothèques tierces :
-
Debounce/Throttle les événements : Si vous utilisez des refs pour mesurer des dimensions sur les événements
window.resizeouscroll, assurez-vous que ces gestionnaires sont "debounced" ou "throttled" pour éviter des appels de fonction excessifs et des lectures du DOM. -
Grouper les lectures/écritures du DOM : Évitez d'entrelacer les opérations de lecture du DOM (par exemple,
getBoundingClientRect()) avec les opérations d'écriture du DOM (par exemple, la définition de styles). Cela peut déclencher un "layout thrashing". Des outils commefastdompeuvent aider à gérer cela efficacement. -
Différer les opérations non critiques : Utilisez
requestAnimationFramepour les animations etsetTimeout(..., 0)ourequestIdleCallbackpour les manipulations DOM moins critiques afin de garantir qu'elles ne bloquent pas le thread principal et n'impactent pas la réactivité. - Choisir judicieusement : Parfois, la performance d'une bibliothèque tierce peut être un goulot d'étranglement. Évaluez des alternatives ou envisagez de charger paresseusement de tels composants pour les utilisateurs sur des connexions plus lentes, garantissant qu'une expérience de base reste performante globalement.
2. Améliorer l'accessibilité (attributs ARIA et navigation au clavier)
Les refs sont essentielles pour construire des applications web très accessibles, en particulier lors de la création de composants d'interface utilisateur personnalisés qui n'ont pas d'équivalents natifs dans le navigateur ou lors de la surcharge des comportements par défaut. Pour un public mondial, le respect des Web Content Accessibility Guidelines (WCAG) n'est pas seulement une bonne pratique, mais souvent une exigence légale. Les refs permettent :
- Gestion programmatique du focus : Comme on l'a vu avec les champs de saisie, les refs vous permettent de définir le focus, ce qui est crucial pour les utilisateurs de clavier et la navigation par lecteur d'écran. Cela inclut la gestion du focus dans les modales, les menus déroulants ou les widgets interactifs.
-
Attributs ARIA dynamiques : Vous pouvez utiliser des refs pour ajouter ou mettre à jour dynamiquement des attributs ARIA (Accessible Rich Internet Applications) (par exemple,
aria-expanded,aria-controls,aria-live) sur des éléments DOM. Cela fournit des informations sémantiques aux technologies d'assistance qui pourraient ne pas être déductibles de l'interface utilisateur visuelle seule.class CollapsibleSection extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
this.state = { isExpanded: false };
}
toggleExpanded = () => {
this.setState(prevState => ({ isExpanded: !prevState.isExpanded }), () => {
if (this.buttonRef.current) {
// Mettre à jour l'attribut ARIA dynamiquement en fonction de l'état
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
});
};
componentDidMount() {
if (this.buttonRef.current) {
this.buttonRef.current.setAttribute('aria-controls', `section-${this.props.id}`);
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
}
render() {
const { id, title, children } = this.props;
const { isExpanded } = this.state;
return (
<div style={{ margin: '20px auto', maxWidth: '600px', border: '1px solid #0056b3', borderRadius: '8px', background: '#e7f0fa', overflow: 'hidden' }}>
<h4>
<button
ref={this.buttonRef} // Ref vers le bouton pour les attributs ARIA
onClick={this.toggleExpanded}
style={{ background: 'none', border: 'none', padding: '15px 20px', width: '100%', textAlign: 'left', cursor: 'pointer', fontSize: '1.2em', color: '#0056b3', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
id={`section-header-${id}`}
>
{title} <span>▼</span>
</button>
</h4>
{isExpanded && (
<div id={`section-${id}`} role="region" aria-labelledby={`section-header-${id}`} style={{ padding: '0 20px 20px', borderTop: '1px solid #a7d9f7' }}>
{children}
</div>
)}
</div>
);
}
} - Contrôle des interactions au clavier : Pour les menus déroulants personnalisés, les curseurs ou autres éléments interactifs, vous pourriez avoir besoin d'implémenter des gestionnaires d'événements clavier spécifiques (par exemple, les touches fléchées pour la navigation dans une liste). Les refs donnent accès à l'élément DOM cible où ces écouteurs d'événements peuvent être attachés et gérés.
En appliquant judicieusement les refs, les développeurs peuvent s'assurer que leurs applications sont utilisables et inclusives pour les personnes handicapées dans le monde entier, élargissant considérablement leur portée et leur impact mondial.
3. Internationalisation (I18n) et interactions localisées
Lorsque l'on travaille avec l'internationalisation (i18n), les refs peuvent jouer un rôle subtil mais important. Par exemple, dans les langues qui utilisent une écriture de droite à gauche (RTL) (comme l'arabe, l'hébreu ou le persan), l'ordre de tabulation naturel et la direction de défilement peuvent différer des langues de gauche à droite (LTR). Si vous gérez par programmation le focus ou le défilement à l'aide de refs, il est crucial de s'assurer que votre logique respecte la direction du texte du document ou de l'élément (attribut dir).
- Gestion du focus sensible au RTL : Bien que les navigateurs gèrent généralement correctement l'ordre de tabulation par défaut pour le RTL, si vous implémentez des pièges à focus personnalisés ou une mise au point séquentielle, testez minutieusement votre logique basée sur les refs dans des environnements RTL pour garantir une expérience cohérente et intuitive.
-
Mesure de la mise en page en RTL : Lors de l'utilisation de
getBoundingClientRect()via une ref, sachez que les propriétésleftetrightsont relatives à la fenêtre d'affichage. Pour les calculs de mise en page qui dépendent du début/fin visuel, tenez compte dudocument.dirou du style calculé de l'élément pour ajuster votre logique pour les mises en page RTL. - Intégration de bibliothèques tierces : Assurez-vous que toutes les bibliothèques tierces intégrées via des refs (par exemple, les bibliothèques de graphiques) sont elles-mêmes conscientes de l'i18n et gèrent correctement les mises en page RTL si votre application les prend en charge. La responsabilité de s'en assurer incombe souvent au développeur qui intègre la bibliothèque dans un composant React.
Conclusion : maîtriser le contrôle impératif avec `createRef` pour les applications mondiales
React.createRef() est plus qu'une simple "échappatoire" dans React ; c'est un outil vital qui comble le fossé entre le puissant paradigme déclaratif de React et les réalités impératives des interactions avec le DOM du navigateur. Bien que son rôle dans les composants fonctionnels plus récents ait été largement repris par le hook useRef, createRef reste la manière standard et la plus idiomatique de gérer les refs au sein des composants de classe, qui constituent encore une part substantielle de nombreuses applications d'entreprise dans le monde.
En comprenant parfaitement sa création, son attachement et le rôle essentiel de la propriété .current, les développeurs peuvent aborder en toute confiance des défis tels que la gestion programmatique du focus, le contrôle direct des médias, l'intégration transparente avec diverses bibliothèques tierces (des graphiques D3.js aux éditeurs de texte enrichi personnalisés) et la mesure précise des dimensions des éléments. Ces capacités ne sont pas seulement des prouesses techniques ; elles sont fondamentales pour créer des applications performantes, accessibles et conviviales pour un vaste éventail d'utilisateurs, d'appareils et de contextes culturels à l'échelle mondiale.
N'oubliez pas de manier ce pouvoir avec discernement. Privilégiez toujours le système déclaratif d'état et de props de React en premier lieu. Lorsque le contrôle impératif est vraiment nécessaire, createRef (pour les composants de classe) ou useRef (pour les composants fonctionnels) offre un mécanisme robuste et bien défini pour y parvenir. La maîtrise des refs vous permet de gérer les cas limites et les subtilités du développement web moderne, garantissant que vos applications React peuvent offrir des expériences utilisateur exceptionnelles partout dans le monde, tout en maintenant les avantages fondamentaux de l'élégante architecture basée sur les composants de React.
Pour aller plus loin et explorer
- Documentation officielle de React sur les Refs : Pour les informations les plus à jour directement de la source, consultez <em>https://react.dev/learn/manipulating-the-dom-with-refs</em>
- Comprendre le Hook `useRef` de React : Pour approfondir l'équivalent pour les composants fonctionnels, explorez <em>https://react.dev/reference/react/useRef</em>
- Transfert de Ref avec `forwardRef` : Apprenez à passer des refs à travers les composants efficacement : <em>https://react.dev/reference/react/forwardRef</em>
- Web Content Accessibility Guidelines (WCAG) : Essentiel pour le développement web mondial : <em>https://www.w3.org/WAI/WCAG22/quickref/</em>
- Optimisation des performances de React : Bonnes pratiques pour des applications performantes : <em>https://react.dev/learn/optimizing-performance</em>